package org.aksw.xoperator.security;

import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;

import org.aksw.xoperator.conf.AgentConfiguration;
import org.aksw.xoperator.sparql.p2p.SPARQLQueryOverXmpp;
import org.aksw.xoperator.sparql.p2p.SPARQLResultoverXmpp;
import org.aksw.xoperator.xmpp.JabberClientManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jivesoftware.smack.RosterGroup;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.util.StringUtils;

import com.hp.hpl.jena.query.QueryFactory;
import com.hp.hpl.jena.query.QuerySolution;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.query.ResultSetFormatter;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.sparql.engine.binding.Binding;

public class SimpleSecurityProvider implements IAccessControl {

	private static Log log = LogFactory.getLog(SimpleSecurityProvider.class);

	private AgentConfiguration conf;

	JabberClientManager jcmanager;

	public SimpleSecurityProvider(AgentConfiguration conf,
			JabberClientManager jcmanager) {
		this.conf = conf;
		this.jcmanager = jcmanager;
	}

	public boolean allow(Message message) {
		boolean allowed = false;

		String fromAddress = StringUtils.parseBareAddress(message.getFrom());
		String masterAddress = conf.getJabberClientConfiguration()
				.getMainAccount().getJid();
		// check first, if the message is from master to agent, this is always
		// allowed
		if (fromAddress.equals(masterAddress)) {
			allowed = true;
		} else {
			// check then if the user is otherwise allowed to communicate

			// collect data for the check
			Pattern groupPattern = conf.getSecurityConfiguration()
					.getAllowGroupsToChat();
			List<Pattern> userPatterns = conf.getSecurityConfiguration()
					.getAllowUsersToChat();
			Collection<RosterGroup> rosterGroups = jcmanager.getRoster()
					.getEntry(fromAddress).getGroups();

			// execute the check
			allowed = validateRegexes(fromAddress, rosterGroups, groupPattern,
					userPatterns);

		}
		return allowed;
	}

	public boolean allow(SPARQLQueryOverXmpp query) {

		// collect data for the check
		String fromAddress = StringUtils.parseBareAddress(query.getFrom());
		Pattern groupPattern = conf.getSecurityConfiguration()
				.getAllowGroupsP2PQuery();
		List<Pattern> userPatterns = conf.getSecurityConfiguration()
				.getAllowUsersP2PQuery();
		Collection<RosterGroup> rosterGroups = jcmanager.getRoster().getEntry(
				fromAddress).getGroups();

		// execute the check

		return validateRegexes(fromAddress, rosterGroups, groupPattern, userPatterns);
	}

	private static boolean validateRegexes(String fromAddress,
			Collection<RosterGroup> rosterGroups, Pattern groupPattern,
			List<Pattern> userPatterns) {
		// check if the user is in the allowed roster
		// iterate through these roster and check if they match
		for (RosterGroup rosterGroup : rosterGroups) {
			// if they do, allow the user
			if (groupPattern.matcher(rosterGroup.getName()).matches()) {
				return true;
			}
		}
		// now we need to iterate only if allowed has not yet been set to true
		// yet.
		// check if any of the userAllow regex is matching, iterate of the
		// patterns and look for a match
		for (Pattern pattern : userPatterns) {
			if (pattern.matcher(fromAddress).matches()) {
				return true;
			}
		}
		// if we got till here, nobody allowed fromAdress

		return false;

	}

	public Message getDenialResponse(Message message) {
		Message response = new Message(message.getFrom(), Message.Type.error);

		response.setBody(conf.getSecurityConfiguration()
				.getAccessDeniedChatMessage());

		return response;
	}

	public IQ getDenialResponse(SPARQLQueryOverXmpp query) {
		String denialMessage = conf.getSecurityConfiguration()
				.getAccessDeniedP2PMessage();
		IQ packet = null;
		if (denialMessage != null && !denialMessage.isEmpty()) {
			packet = new SPARQLResultoverXmpp(query.getTo(),
					XMPPError.Condition.forbidden, denialMessage);
		} else {
			// create a
			log
					.warn("Unable to return a P2P error message, as feature is unimplemented now, leave the accessDeniedP2PMessage field in the conf empty");

		}

		return packet;
	}

	/**
	 * creating a mock, empty result set for a given query
	 * 
	 * @param query
	 * @return
	 */
	public static String emptyResultForQuery(SPARQLQueryOverXmpp query) {
		final List resultvars = QueryFactory.create(query.getQuery())
				.getResultVars();

		return ResultSetFormatter.asXMLString(new ResultSet() {

			public Model getResourceModel() {
				// TODO Auto-generated method stub
				return null;
			}

			public List getResultVars() {
				// TODO Auto-generated method stub
				return resultvars;
			}

			public int getRowNumber() {
				// TODO Auto-generated method stub
				return 0;
			}

			public boolean hasNext() {
				// TODO Auto-generated method stub
				return false;
			}

			public boolean isOrdered() {
				// TODO Auto-generated method stub
				return false;
			}

			public Object next() {
				// TODO Auto-generated method stub
				return null;
			}

			public Binding nextBinding() {
				// TODO Auto-generated method stub
				return null;
			}

			public QuerySolution nextSolution() {
				// TODO Auto-generated method stub
				return null;
			}

			public void remove() {
				// TODO Auto-generated method stub

			}

		});

	}

}
